package com.aaaa.scheduler.scheduler.genetic;


import com.aaaa.scheduler.manager.impl.SchedulerManager;
import com.aaaa.scheduler.pojo.CandidateMachine;
import com.aaaa.scheduler.pojo.Chromosome;
import com.aaaa.scheduler.pojo.Instance;
import com.aaaa.scheduler.pojo.Process;
import com.aaaa.scheduler.scheduler.nsga.NSGAII;
import com.aaaa.scheduler.scheduler.nsga.NSGAIII;
import com.aaaa.scheduler.util.InstanceUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.SerializationUtils;
import org.jfree.data.xy.DefaultXYDataset;
import tanling.matplot3d.common.Point3D;

import java.util.*;


/**
 * Description:遗传算法
 *
 * @author zll-hust E-mail:zh20010728@126.com
 * @date 创建时间：2020年5月28日 下午2:58:10
 */
@EqualsAndHashCode(callSuper = true)
@Data
@AllArgsConstructor
public class GAScheduleManager extends SchedulerManager {
    private Random r;



    public GAScheduleManager() {
        super();
        this.r = new Random();
//		this.r.setSeed(1);
    }

    /**
     * 功能描述：NSGA-III算法
     *
     */
    public void nsga3Schedule(Instance instance, int mode) throws Exception {
        //初始化配置 横纵竖坐标 种群大小 迭代次数
        GeneticConfiguration.configure();

        //三维散点图的数据集
        List<List<Point3D>> dataSet = new ArrayList<>();

        // 随机生成初始种群
        Chromosome[] parents = genePopulations(instance);
        NSGAII.fastNonDominatedSort(parents);//快速非支配排序 拥挤度计算 rank排名
        Chromosome[] children;
        Chromosome[] combinedPopulation;//父种群和子种群的合体

        int noImprove = 0;//提升后的第一代 或 为了避免局部最优添加扰动之后的第一代
        int gen = 1;//当前迭代次数
        int zeng = GeneticConfiguration.MAX_GENERATIONS/10;//只记录5代
        while (gen < GeneticConfiguration.MAX_GENERATIONS + 1) {

            System.out.println("第 " + gen + " 代");

            //TODO 怎么表达4维目标 5维目标
            if(GeneticConfiguration.objectiveNum == 3 && gen%zeng==0){//只有三个目标才可以画3D图 三个以上目标暂时不知道怎么表达
                List<Point3D> point3DS = new ArrayList<>();
                for (Chromosome parent : parents) {
                    List<Double> objectives = parent.getObjectiveFitnessValueList();
                    point3DS.add(new Point3D(objectives.get(0), objectives.get(1), objectives.get(2)));
                }
                dataSet.add(point3DS);
            }

            // 陷入局部最优时进行扰动：取部分精英个体后 其余随机生成新个体
            if (gen - noImprove > GeneticConfiguration.MAX_NO_IMPROVE) {
                System.out.println("第" + gen +"代扰动一次");
                disturbance(parents, instance);
                noImprove = gen;
            }

            children = oneCrossMuta(parents, instance);//生成第一代后代
            combinedPopulation = createCombinedPopulation(parents, children);
            NSGAII.fastNonDominatedSort(combinedPopulation);
            int lastRank = combinedPopulation[GeneticConfiguration.POPULATION_SIZE-1].getRank();
            int length = combinedPopulation.length;
            for(int i=GeneticConfiguration.POPULATION_SIZE; i<combinedPopulation.length; i++){
                if(combinedPopulation[i].getRank() != lastRank){//找到lastRank下一层的第一个个体位置
                    length = i;
                    break;
                }
            }
            Chromosome[] needChosenPopulation = new Chromosome[length];
            for (int i = 0; i < length; i++) {
                needChosenPopulation[i] = combinedPopulation[i];
            }
            if(length == GeneticConfiguration.POPULATION_SIZE){
                parents = needChosenPopulation;
            }else{
                parents = NSGAIII.associateAndNiching(needChosenPopulation);
            }

            gen++;
        }

//        for (Chromosome parent : parents) {
//            System.out.println(parent.getObjectiveFitnessValueList() + " ");
//        }
        Chromosome best = getBest(parents);
        System.out.println(best.getObjectiveFitnessValueList());

        //最优种群保存在parents中，最后可以把这些个体按照三个目标的值 画在坐标中
        //TODO 可以给每个个体一个id，这样用户可以从坐标中选择想要的个体 最为最终结果
        InstanceUtil.render3DGraphWithDataSets(dataSet);

    }


    /**
     * 功能描述：NSGA-II算法
     *
     */
    public void nsga2Schedule(Instance instance, int mode) throws Exception {
        //初始化配置 横纵坐标 种群大小 迭代次数
        GeneticConfiguration.configure();

        DefaultXYDataset xydataset = new DefaultXYDataset();
        //根绝实际需求加载数据集到xydatasets中
        double[][][] datas = new double[GeneticConfiguration.MAX_GENERATIONS+1][2][GeneticConfiguration.POPULATION_SIZE];

        // 随机生成初始种群
        Chromosome[] parents = genePopulations(instance);
        NSGAII.fastNonDominatedSort(parents);//快速非支配排序 拥挤度计算 rank排名
        Chromosome[] children;
        Chromosome[] combinedPopulation;//父种群和子种群的合体

        int noImprove = 0;//提升后的第一代 或 为了避免局部最优添加扰动之后的第一代
        int gen = 1;//当前迭代次数
        int firstNoImprove = 0;//每次进化提升后的第一代    当前代数-提升后第一代 = 多少代没有提升
        while (gen < GeneticConfiguration.MAX_GENERATIONS + 1) {

            System.out.println("第 " + gen + " 代");

            for (int i = 0; i < parents.length; i++) {
                datas[gen][0][i] = parents[i].getObjectiveFitnessValueList().get(0);
                datas[gen][1][i] = parents[i].getObjectiveFitnessValueList().get(1);
            }

            // 陷入局部最优时进行扰动：取部分精英个体后 其余随机生成新个体
            if (gen - noImprove > GeneticConfiguration.MAX_NO_IMPROVE) {
                System.out.println("第" + gen +"代扰动一次");
                disturbance(parents, instance);
                noImprove = gen;
            }

            children = oneCrossMuta(parents, instance);//生成第一代后代
            combinedPopulation = createCombinedPopulation(parents, children);
            NSGAII.fastNonDominatedSort(combinedPopulation);
            parents = NSGAII.getChildAfterCrowdingDistanceAssignment(combinedPopulation);

            gen++;
        }

//        for (Chromosome parent : parents) {
//            System.out.println(parent.getObjectiveFitnessValueList() + " ");
//        }
        Chromosome best = getBest(parents);
        System.out.println(best.getObjectiveFitnessValueList());

        //最优种群保存在parents中，最后可以把这些个体按照两个目标的值 画在坐标中
        //TODO 可以给每个个体一个id，这样用户可以从坐标中选择想要的个体 最为最终结果
//        InstanceUtil.render2DGraph(parents);
        for (int i = datas.length - 1; i >= 1; i-=2) {
            xydataset.addSeries(i, datas[i]);  //l为类别标签
        }
        InstanceUtil.render2DGraphWithDataSets(xydataset);

        //动态显示每一步的分派结果
        instance.setWirteDynamic(true);
        //解码  记录每一步的调度情况
        evaluateOnChromosomeFinal(best, instance);

    }

    /**
     * 功能描述：对种群进行一次扰动
     *
     */
    private void disturbance(Chromosome[] parents, Instance instance) throws Exception {
        int num = (int) (GeneticConfiguration.PRETURBATION_PROBABILITY * GeneticConfiguration.POPULATION_SIZE);
        ArrayList<Chromosome> p = new ArrayList<>();
        Collections.addAll(p, parents);
        Collections.sort(p);
//        for (Chromosome chromosome : p) {
//            System.out.print(chromosome.getObjectiveFitnessValueList() + " ");
//        }
//        System.out.println();
        for (int i = 0; i < num; i++)
            parents[i] = p.get(i);
        for (int i = num; i < GeneticConfiguration.POPULATION_SIZE; i++) {
            parents[i] = new Chromosome(instance, r);
            evaluateOnChromosome(parents[i], instance);
        }
    }


    /**
     * 功能描述：遗传算法
     *
     */
    public void gaSchedule(Instance instance, int mode) throws Exception {
        GeneticConfiguration.configure();
        ChromosomeOperation chromOps = new ChromosomeOperation(this.r);

        // 随机生成初始种群
        Chromosome[] parents = genePopulations(instance);
        Chromosome[] children = new Chromosome[GeneticConfiguration.POPULATION_SIZE];
        for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++) {
            children[i] = new Chromosome(parents[i]);
        }

        // 获取最优子代
        Chromosome best = getBest(parents);
        Chromosome currentBest = new Chromosome(best);

        int noImprove = 0;//提升后的第一代 或 为了避免局部最优添加扰动之后的第一代
        int gen = 0;//当前迭代次数
        int firstNoImprove = 0;//每次进化提升后的第一代    当前代数-提升后第一代 = 多少代没有提升
        while (gen < GeneticConfiguration.MAX_GENERATIONS) {

            // 陷入局部最优时进行扰动：取部分精英个体后 其余随机生成新个体
            if (gen - noImprove > GeneticConfiguration.MAX_NO_IMPROVE) {
                System.out.println("扰动一次");
                disturbance(parents, instance);
                noImprove = gen;
            }

            // 选择 selection
            children = chromOps.Selection(parents, GeneticConfiguration.REPRODUCTION_PROBABILITY);

            // 交叉 cross
            for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE-1; i += 2) {
                if (r.nextDouble() < GeneticConfiguration.CROSSOVER_PROBABILITY) {
//					int fatherIndex = r.nextInt(POPULATION_SIZE);
//					int motherIndex = r.nextInt(POPULATION_SIZE);
//					while (fatherIndex == motherIndex)
//						motherIndex = r.nextInt(POPULATION_SIZE);
                    int fatherIndex = i;
                    int motherIndex = i + 1;
                    chromOps.Crossover(children[fatherIndex], children[motherIndex], instance.getTotalProductNum());
                }
            }

            // 变异 mutation
            for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++) {
                if (r.nextDouble() < GeneticConfiguration.MUTATION_PROBABILITY) {
                    chromOps.Mutation(children[i], instance.getCanMachineNumList());
                }
            }

            // 计算所有个体的适应度
            for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++){
                evaluateOnChromosome(children[i], instance);
                parents[i] = new Chromosome(children[i]);
            }
//            for (int i = 0; i < this.POPULATION_SIZE; i++) {
//                evaluateOnChromosome(children[i], instance);
//                int maxTSIterSize = (int) (MAX_GENERATIONS * ((float) gen / (float) MAX_GENERATIONS));
//                Solution sol = new Solution(operationMatrix, children[i], input, 1.0 / children[i].fitness);
//
//                // TS1
//                sol = NeiborAl2.search(sol, maxTSIterSize);
//
//                // TS2
////                sol = NeighbourAlgorithms.neighbourSearch(sol);
//
//                parents[i] = sol.toChromosome();
//            }

            // 获取最优的染色体
            currentBest = getBest(parents);
//            for (Chromosome parent : parents) {
//                System.out.print(parent.getObjectiveFitnessValueList() + " ");
//            }
//            System.out.println("\n" + currentBest.getObjectiveFitnessValueList());

//             tabu search
//            int tabuNr = POPULATION_SIZE - (int) (pt * POPULATION_SIZE);
//            for (int i = 0; i < tabuNr; i++) {
//                parents[i] = new Chromosome(children[i]);
//            }
//            for (int i = tabuNr; i < POPULATION_SIZE; i++) {
//                int maxTSIterSize = (int) (MAX_GENERATIONS * ((float) gen / (float) MAX_GENERATIONS));
//                parents[i] = new Chromosome(tabu.TabuSearch(maxTSIterSize, 5, children[i], best.fitness));
//            }

            if (currentBest.compareTo(best) < 0) {
                best = new Chromosome(currentBest);
                noImprove = gen;
                firstNoImprove = gen;
                System.out.println("！！！！！！！！在第 " + gen + " 代进化出更好的染色体，适应度为："+ currentBest.getObjectiveFitnessValueList() +"！！！！！！！！");
            }
            System.out.println("经过 " + gen + " 代，当前最优染色体的适应度为:" + best.getObjectiveFitnessValueList());

            gen++;
        }
        System.out.print("经过 " + gen + " 代遗传之后, 最优染色体的调度目标为:   ");
        for (int i = 0; i < GeneticConfiguration.objectiveList.size(); i++) {
            System.out.print(GeneticConfiguration.objectiveList.get(i).getObjectiveName() + ":"
                    + best.getObjectiveFitnessValueList().get(i) + "      ");
        }
        System.out.println();
        System.out.println("在第 " + firstNoImprove + " 代，第一次出现此最优染色体");

        //动态显示每一步的分派结果
        instance.setWirteDynamic(true);
        //解码  记录每一步的调度情况
        evaluateOnChromosomeFinal(best, instance);

    }


    /**
     * 功能描述：只有解码
     */
    public void gaSchedule1(Instance instance, int mode) throws Exception {
        //初始化染色体
        Chromosome chromosome = new Chromosome(instance, this.r);
        //动态显示每一步的分派结果
        instance.setWirteDynamic(true);
        //解码  计算适应度值
        evaluateOnChromosomeFinal(chromosome, instance);
    }

    /**
     * 功能描述：合并两个种群
     *
     */
    private Chromosome[] createCombinedPopulation(Chromosome[] parents, Chromosome[] children) {
        Chromosome[] population = new Chromosome[parents.length + children.length];
        int i = 0;
        for (Chromosome parent : parents) {
            population[i++] = parent;
        }
        for (Chromosome child : children) {
            population[i++] = child;
        }
        return population;
    }

    /**
     * 功能描述：经过一次交叉，变异，产生后代
     *
     */
    private Chromosome[] oneCrossMuta(Chromosome[] parents, Instance instance) throws Exception {
        ChromosomeOperation chromOps = new ChromosomeOperation(this.r);
        // 选择 不用选择 直接用父种群
        Chromosome[] children = copyPopulation(parents);
        // 交叉 cross
        for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE-1; i += 2) {
            if (r.nextDouble() < GeneticConfiguration.CROSSOVER_PROBABILITY) {
                int fatherIndex = i;
                int motherIndex = i + 1;
                chromOps.Crossover(children[fatherIndex], children[motherIndex], instance.getTotalProductNum());
            }
        }

        // 变异 mutation
        for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++) {
            if (r.nextDouble() < GeneticConfiguration.MUTATION_PROBABILITY) {
                chromOps.Mutation(children[i], instance.getCanMachineNumList());
            }
        }

        // 计算所有个体的适应度  不用计算  快速非支配排序 和 拥挤度排序 已经计算了适应度

        //计算每个染色体的所有目标值
        for (Chromosome child : children) {
            evaluateOnChromosome(child, instance);
        }

        return children;
    }

    private Chromosome[] copyPopulation(Chromosome[] parents) {
        Chromosome[] newP = new Chromosome[parents.length];
        for (int i = 0; i < parents.length; i++) {
            newP[i] = new Chromosome(parents[i]);
        }
        return newP;
    }

    /**
     * 功能描述：随机生成一个种群
     *
     */
    private Chromosome[] genePopulations(Instance instance) throws Exception {
        Chromosome[] parents = new Chromosome[GeneticConfiguration.POPULATION_SIZE];// 染色体
        for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++) {
            parents[i] = new Chromosome(instance, this.r);
            evaluateOnChromosome(parents[i], instance);//计算染色体的所有目标
        }
        return parents;
    }

    /**
     * 功能描述：获取染色体种群中的最优染色体
     *
     */
    private Chromosome getBest(Chromosome[] parents) {
//        double maxFitness = Double.NEGATIVE_INFINITY;
//        int index = 0;
//        for (int i = 0; i < GeneticConfiguration.POPULATION_SIZE; i++) {
//            if (maxFitness < parents[i].getFitness()) {
//                index = i;
//                maxFitness = parents[i].getFitness();
//            }
//        }
//        return new Chromosome(parents[index]);
        Chromosome max = parents[0];
        for (int i = 1; i < parents.length; i++) {
            if(parents[i].compareTo(max) < 0){
                max = parents[i];
            }
        }
        return max;
    }

    /**
     * 功能描述：用于遗传算法得出的最优染色体的 解码
     *
     */
    public void evaluateOnChromosomeFinal(Chromosome chromosome, Instance instance) throws Exception {
        System.out.println();
        System.out.println("\n**********************调度每一步的分派情况**********************");
        int[] operNoOfEachJob = new int[instance.getTotalProductNum()];// 当前处理到工件的工序序号
        Arrays.fill(operNoOfEachJob, 0);
        int jobNo = 0;//工件序号
        int operNo = 0;//当前工件的工序序号
        for(int i=0; i<chromosome.getGene_OS().length; i++){
            jobNo =chromosome.getGene_OS()[i];//正常序号
            operNo = operNoOfEachJob[jobNo-1]++;//比正常序号少1
            Process process = instance.getProductMap().get("product:"+jobNo).getOpList().get(operNo);

            int machineIndex = chromosome.getGene_MS()[instance.getProcessToIndex()[jobNo-1].get(operNo)];
            List<CandidateMachine> candidateMachineList = instance.getCandidateMachineMap().get(process.getName());
            CandidateMachine candidateMachine = candidateMachineList.get(machineIndex - 1);

            processManager.assignTask(instance,process,true, true, candidateMachine);

            // 用于保存每步分派结果，每一步的instance都是当前的状态
            if (instance.isWirteDynamic()) {
                InstanceUtil.outputSolutionStep(process);
            }
        }
    }
    /**
     * 功能描述：用于遗传算法 解码染色体 计算目标 适应度
     *
     */
    public void evaluateOnChromosome(Chromosome chromosome, Instance instanceOld) throws Exception {
        Instance instance = SerializationUtils.clone(instanceOld);
        int[] operNoOfEachJob = new int[instance.getTotalProductNum()];// 当前处理到工件的工序序号
        Arrays.fill(operNoOfEachJob, 0);
        int jobNo = 0;//工件序号
        int operNo = 0;//当前工件的工序序号
        for(int i=0; i<chromosome.getGene_OS().length; i++){
            jobNo =chromosome.getGene_OS()[i];//正常序号
            operNo = operNoOfEachJob[jobNo-1]++;//比正常序号少1
            Process process = instance.getProductMap().get("product:"+jobNo).getOpList().get(operNo);

            int machineIndex = chromosome.getGene_MS()[instance.getProcessToIndex()[jobNo-1].get(operNo)];
            List<CandidateMachine> candidateMachineList = instance.getCandidateMachineMap().get(process.getName());
            CandidateMachine candidateMachine = candidateMachineList.get(machineIndex - 1);

            processManager.assignTask(instance,process,true, true, candidateMachine);

            // 用于打印每步分派结果，每一步的instance都是当前的状态
            if (instance.isWirteDynamic()) {
                InstanceUtil.outputSolutionStep(process);
            }
        }
//        System.out.println("**********"+instance.getObjective().getObjectiveValue());
        //用于单目标的适应度  nsga2的双目标的计算适应度在快速非支配排序方法中和拥挤度排序方法中
//        chromosome.setFitness(1.0/instance.getObjectiveList().get(0).getObjectiveValue());
        for (int i = 0; i < GeneticConfiguration.objectiveList.size(); i++) {
            chromosome.getObjectiveFitnessValueList().set(i,instance.getObjectiveList().get(i).getObjectiveValue());
        }
    }
}